home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 4: GNU Archives / Linux Cubed Series 4 - GNU Archives.iso / gnu / cvs-1.8 / cvs-1 / cvs-1.8.1 / src / rcs.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-06  |  54.2 KB  |  2,263 lines

  1. /*
  2.  * Copyright (c) 1992, Brian Berliner and Jeff Polk
  3.  * 
  4.  * You may distribute under the terms of the GNU General Public License as
  5.  * specified in the README file that comes with the CVS 1.4 kit.
  6.  * 
  7.  * The routines contained in this file do all the rcs file parsing and
  8.  * manipulation
  9.  */
  10.  
  11. #include <assert.h>
  12. #include "cvs.h"
  13.  
  14. static RCSNode *RCS_parsercsfile_i PROTO((FILE * fp, const char *rcsfile));
  15. static char *RCS_getdatebranch PROTO((RCSNode * rcs, char *date, char *branch));
  16. static int getrcskey PROTO((FILE * fp, char **keyp, char **valp));
  17. static int checkmagic_proc PROTO((Node *p, void *closure));
  18. static void do_branches PROTO((List * list, char *val));
  19. static void do_symbols PROTO((List * list, char *val));
  20. static void rcsvers_delproc PROTO((Node * p));
  21.  
  22. /*
  23.  * We don't want to use isspace() from the C library because:
  24.  *
  25.  * 1. The definition of "whitespace" in RCS files includes ASCII
  26.  *    backspace, but the C locale doesn't.
  27.  * 2. isspace is an very expensive function call in some implementations
  28.  *    due to the addition of wide character support.
  29.  */
  30. static const char spacetab[] = {
  31.         0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0,    /* 0x00 - 0x0f */
  32.         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1f */
  33.         1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 - 0x2f */
  34.         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 - 0x3f */
  35.         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x4f */
  36.         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */
  37.         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 - 0x8f */
  38.         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 - 0x7f */
  39.         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8f */
  40.         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9f */
  41.         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0 - 0xaf */
  42.         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0 - 0xbf */
  43.         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc0 - 0xcf */
  44.         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0 - 0xdf */
  45.         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0 - 0xef */
  46.         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  /* 0xf0 - 0xff */
  47. };
  48.  
  49. #define whitespace(c)    (spacetab[(unsigned char)c] != 0)
  50.  
  51.  
  52. /*
  53.  * Parse an rcsfile given a user file name and a repository
  54.  */
  55. RCSNode *
  56. RCS_parse (file, repos)
  57.     const char *file;
  58.     const char *repos;
  59. {
  60.     RCSNode *rcs;
  61.     FILE *fp;
  62.     char rcsfile[PATH_MAX];
  63.  
  64.     (void) sprintf (rcsfile, "%s/%s%s", repos, file, RCSEXT);
  65.     if ((fp = fopen (rcsfile, FOPEN_BINARY_READ)) != NULL) 
  66.     {
  67.         rcs = RCS_parsercsfile_i(fp, rcsfile);
  68.     if (rcs != NULL) 
  69.         rcs->flags |= VALID;
  70.  
  71.     fclose (fp);
  72.     return (rcs);
  73.     }
  74.     else if (! existence_error (errno))
  75.     {
  76.     error (0, errno, "cannot open %s", rcsfile);
  77.     return NULL;
  78.     }
  79.  
  80.     (void) sprintf (rcsfile, "%s/%s/%s%s", repos, CVSATTIC, file, RCSEXT);
  81.     if ((fp = fopen (rcsfile, FOPEN_BINARY_READ)) != NULL) 
  82.     {
  83.         rcs = RCS_parsercsfile_i(fp, rcsfile);
  84.     if (rcs != NULL)
  85.     {
  86.         rcs->flags |= INATTIC;
  87.         rcs->flags |= VALID;
  88.     }
  89.  
  90.     fclose (fp);
  91.     return (rcs);
  92.     }
  93.     else if (! existence_error (errno))
  94.     {
  95.     error (0, errno, "cannot open %s", rcsfile);
  96.     return NULL;
  97.     }
  98.  
  99.     return (NULL);
  100. }
  101.  
  102. /*
  103.  * Parse a specific rcsfile.
  104.  */
  105. RCSNode *
  106. RCS_parsercsfile (rcsfile)
  107.     char *rcsfile;
  108. {
  109.     FILE *fp;
  110.     RCSNode *rcs;
  111.  
  112.     /* open the rcsfile */
  113.     if ((fp = fopen (rcsfile, FOPEN_BINARY_READ)) == NULL)
  114.     {
  115.     error (0, errno, "Couldn't open rcs file `%s'", rcsfile);
  116.     return (NULL);
  117.     }
  118.  
  119.     rcs = RCS_parsercsfile_i (fp, rcsfile);
  120.  
  121.     fclose (fp);
  122.     return (rcs);
  123. }
  124.  
  125.  
  126. /*
  127.  */ 
  128. static RCSNode *
  129. RCS_parsercsfile_i (fp, rcsfile)
  130.     FILE *fp;
  131.     const char *rcsfile;
  132. {
  133.     RCSNode *rdata;
  134.     char *key, *value;
  135.  
  136.     /* make a node */
  137.     rdata = (RCSNode *) xmalloc (sizeof (RCSNode));
  138.     memset ((char *) rdata, 0, sizeof (RCSNode));
  139.     rdata->refcount = 1;
  140.     rdata->path = xstrdup (rcsfile);
  141.  
  142.     /* Process HEAD and BRANCH keywords from the RCS header.  
  143.      *
  144.      * Most cvs operatations on the main branch don't need any more
  145.      * information.  Those that do call XXX to completely parse the
  146.      * RCS file.  */
  147.  
  148.     if (getrcskey (fp, &key, &value) == -1 || key == NULL)
  149.     goto l_error;
  150.     if (strcmp (key, RCSDESC) == 0)
  151.     goto l_error;
  152.  
  153.     if (strcmp (RCSHEAD, key) == 0 && value != NULL)
  154.     rdata->head = xstrdup (value);
  155.  
  156.     if (getrcskey (fp, &key, &value) == -1 || key == NULL)
  157.     goto l_error;
  158.     if (strcmp (key, RCSDESC) == 0)
  159.     goto l_error;
  160.  
  161.     if (strcmp (RCSBRANCH, key) == 0 && value != NULL)
  162.     {
  163.     char *cp;
  164.  
  165.     rdata->branch = xstrdup (value);
  166.     if ((numdots (rdata->branch) & 1) != 0)
  167.     {
  168.         /* turn it into a branch if it's a revision */
  169.         cp = strrchr (rdata->branch, '.');
  170.         *cp = '\0';
  171.     }
  172.     }
  173.  
  174.     rdata->flags |= PARTIAL;
  175.     return rdata;
  176.  
  177. l_error:
  178.     if (!really_quiet)
  179.     {
  180.     if (ferror(fp))
  181.     {
  182.         error (1, 0, "error reading `%s'", rcsfile);
  183.     }
  184.     else
  185.     {
  186.         error (0, 0, "`%s' does not appear to be a valid rcs file",
  187.            rcsfile);
  188.     }
  189.     }
  190.     freercsnode (&rdata);
  191.     return (NULL);
  192. }
  193.  
  194.  
  195. /* Do the real work of parsing an RCS file.
  196.  
  197.    On error, die with a fatal error; if it returns at all it was successful.
  198.  
  199.    If PFP is NULL, close the file when done.  Otherwise, leave it open
  200.    and store the FILE * in *PFP.  */
  201. static void
  202. RCS_reparsercsfile (rdata, pfp)
  203.     RCSNode *rdata;
  204.     FILE **pfp;
  205. {
  206.     FILE *fp;
  207.     char *rcsfile;
  208.  
  209.     Node *q;
  210.     RCSVers *vnode;
  211.     int n;
  212.     char *cp;
  213.     char *key, *value;
  214.  
  215.     assert (rdata != NULL);
  216.     rcsfile = rdata->path;
  217.  
  218.     fp = fopen(rcsfile, FOPEN_BINARY_READ);
  219.     if (fp == NULL)
  220.     error (1, 0, "unable to reopen `%s'", rcsfile);
  221.  
  222.     /* make a node */
  223.     rdata->versions = getlist ();
  224.  
  225.     /*
  226.      * process all the special header information, break out when we get to
  227.      * the first revision delta
  228.      */
  229.     for (;;)
  230.     {
  231.     /* get the next key/value pair */
  232.  
  233.     /* if key is NULL here, then the file is missing some headers
  234.        or we had trouble reading the file. */
  235.     if (getrcskey (fp, &key, &value) == -1 || key == NULL
  236.         || strcmp (key, RCSDESC) == 0)
  237.     {
  238.         if (ferror(fp))
  239.         {
  240.         error (1, 0, "error reading `%s'", rcsfile);
  241.         }
  242.         else
  243.         {
  244.         error (1, 0, "`%s' does not appear to be a valid rcs file",
  245.                rcsfile);
  246.         }
  247.     }
  248.  
  249.     if (strcmp (RCSSYMBOLS, key) == 0)
  250.     {
  251.         if (value != NULL)
  252.         {
  253.         rdata->symbols_data = xstrdup(value);
  254.         continue;
  255.         }
  256.     }
  257.  
  258.     if (strcmp (RCSEXPAND, key) == 0)
  259.     {
  260.         rdata->expand = xstrdup (value);
  261.         continue;
  262.     }
  263.  
  264.     /*
  265.      * check key for '.''s and digits (probably a rev) if it is a
  266.      * revision, we are done with the headers and are down to the
  267.      * revision deltas, so we break out of the loop
  268.      */
  269.     for (cp = key; (isdigit (*cp) || *cp == '.') && *cp != '\0'; cp++)
  270.          /* do nothing */ ;
  271.     if (*cp == '\0' && strncmp (RCSDATE, value, strlen (RCSDATE)) == 0)
  272.         break;
  273.  
  274.     /* if we haven't grabbed it yet, we didn't want it */
  275.     }
  276.  
  277.     /*
  278.      * we got out of the loop, so we have the first part of the first
  279.      * revision delta in our hand key=the revision and value=the date key and
  280.      * its value
  281.      */
  282.     for (;;)
  283.     {
  284.     char *valp;
  285.  
  286.         vnode = (RCSVers *) xmalloc (sizeof (RCSVers));
  287.     memset (vnode, 0, sizeof (RCSVers));
  288.  
  289.     /* fill in the version before we forget it */
  290.     vnode->version = xstrdup (key);
  291.  
  292.     /* grab the value of the date from value */
  293.     valp = value + strlen (RCSDATE);/* skip the "date" keyword */
  294.     while (whitespace (*valp))        /* take space off front of value */
  295.         valp++;
  296.  
  297.     vnode->date = xstrdup (valp);
  298.  
  299.     /* Get author field.  */
  300.     (void) getrcskey (fp, &key, &value);
  301.     /* FIXME: should be using errno in case of ferror.  */
  302.     if (key == NULL || strcmp (key, "author") != 0)
  303.         error (1, 0, "\
  304. unable to parse rcs file; `author' not in the expected place");
  305.     vnode->author = xstrdup (value);
  306.  
  307.     /* Get state field.  */
  308.     (void) getrcskey (fp, &key, &value);
  309.     /* FIXME: should be using errno in case of ferror.  */
  310.     if (key == NULL || strcmp (key, "state") != 0)
  311.         error (1, 0, "\
  312. unable to parse rcs file; `state' not in the expected place");
  313.     if (strcmp (value, "dead") == 0)
  314.     {
  315.         vnode->dead = 1;
  316.     }
  317.  
  318.     /* fill in the branch list (if any branches exist) */
  319.     (void) getrcskey (fp, &key, &value);
  320.     /* FIXME: should be handling various error conditions better.  */
  321.     if (key != NULL && strcmp (key, RCSDESC) == 0)
  322.         value = NULL;
  323.     if (value != (char *) NULL)
  324.     {
  325.         vnode->branches = getlist ();
  326.         do_branches (vnode->branches, value);
  327.     }
  328.  
  329.     /* fill in the next field if there is a next revision */
  330.     (void) getrcskey (fp, &key, &value);
  331.     /* FIXME: should be handling various error conditions better.  */
  332.     if (key != NULL && strcmp (key, RCSDESC) == 0)
  333.         value = NULL;
  334.     if (value != (char *) NULL)
  335.         vnode->next = xstrdup (value);
  336.  
  337.     /*
  338.      * at this point, we skip any user defined fields XXX - this is where
  339.      * we put the symbolic link stuff???
  340.      */
  341.     /* FIXME: Does not correctly handle errors, e.g. from stdio.  */
  342.     while ((n = getrcskey (fp, &key, &value)) >= 0)
  343.     {
  344.         assert (key != NULL);
  345.  
  346.         if (strcmp (key, RCSDESC) == 0)
  347.         {
  348.         n = -1;
  349.         break;
  350.         }
  351.  
  352.         /* Enable use of repositories created by certain obsolete
  353.            versions of CVS.  This code should remain indefinately;
  354.            there is no procedure for converting old repositories, and
  355.            checking for it is harmless.  */
  356.         if (strcmp(key, RCSDEAD) == 0)
  357.         {
  358.         vnode->dead = 1;
  359.         continue;
  360.         }
  361.         /* if we have a revision, break and do it */
  362.         for (cp = key; (isdigit (*cp) || *cp == '.') && *cp != '\0'; cp++)
  363.          /* do nothing */ ;
  364.         if (*cp == '\0' && strncmp (RCSDATE, value, strlen (RCSDATE)) == 0)
  365.         break;
  366.     }
  367.  
  368.     /* get the node */
  369.     q = getnode ();
  370.     q->type = RCSVERS;
  371.     q->delproc = rcsvers_delproc;
  372.     q->data = (char *) vnode;
  373.     q->key = vnode->version;
  374.  
  375.     /* add the nodes to the list */
  376.     if (addnode (rdata->versions, q) != 0)
  377.     {
  378. #if 0
  379.         purify_printf("WARNING: Adding duplicate version: %s (%s)\n",
  380.              q->key, rcsfile);
  381.         freenode (q);
  382. #endif
  383.     }
  384.  
  385.     /*
  386.      * if we left the loop because there were no more keys, we break out
  387.      * of the revision processing loop
  388.      */
  389.     if (n < 0)
  390.         break;
  391.     }
  392.  
  393.     if (pfp == NULL)
  394.     {
  395.     if (fclose (fp) < 0)
  396.         error (0, errno, "cannot close %s", rcsfile);
  397.     }
  398.     else
  399.     {
  400.     *pfp = fp;
  401.     }
  402.     rdata->flags &= ~PARTIAL;
  403. }
  404.  
  405. /*
  406.  * freercsnode - free up the info for an RCSNode
  407.  */
  408. void
  409. freercsnode (rnodep)
  410.     RCSNode **rnodep;
  411. {
  412.     if (rnodep == NULL || *rnodep == NULL)
  413.     return;
  414.  
  415.     ((*rnodep)->refcount)--;
  416.     if ((*rnodep)->refcount != 0)
  417.     {
  418.     *rnodep = (RCSNode *) NULL;
  419.     return;
  420.     }
  421.     free ((*rnodep)->path);
  422.     dellist (&(*rnodep)->versions);
  423.     if ((*rnodep)->symbols != (List *) NULL)
  424.     dellist (&(*rnodep)->symbols);
  425.     if ((*rnodep)->symbols_data != (char *) NULL)
  426.     free ((*rnodep)->symbols_data);
  427.     if ((*rnodep)->expand != NULL)
  428.     free ((*rnodep)->expand);
  429.     if ((*rnodep)->head != (char *) NULL)
  430.     free ((*rnodep)->head);
  431.     if ((*rnodep)->branch != (char *) NULL)
  432.     free ((*rnodep)->branch);
  433.     free ((char *) *rnodep);
  434.     *rnodep = (RCSNode *) NULL;
  435. }
  436.  
  437. /*
  438.  * rcsvers_delproc - free up an RCSVers type node
  439.  */
  440. static void
  441. rcsvers_delproc (p)
  442.     Node *p;
  443. {
  444.     RCSVers *rnode;
  445.  
  446.     rnode = (RCSVers *) p->data;
  447.  
  448.     if (rnode->branches != (List *) NULL)
  449.     dellist (&rnode->branches);
  450.     if (rnode->date != (char *) NULL)
  451.     free (rnode->date);
  452.     if (rnode->next != (char *) NULL)
  453.     free (rnode->next);
  454.     free ((char *) rnode);
  455. }
  456.  
  457. /*
  458.  * getrcskey - fill in the key and value from the rcs file the algorithm is
  459.  *             as follows 
  460.  *
  461.  *    o skip whitespace o fill in key with everything up to next white 
  462.  *      space or semicolon 
  463.  *    o if key == "desc" then key and data are NULL and return -1 
  464.  *    o if key wasn't terminated by a semicolon, skip white space and fill 
  465.  *      in value with everything up to a semicolon 
  466.  *    o compress all whitespace down to a single space 
  467.  *    o if a word starts with @, do funky rcs processing
  468.  *    o strip whitespace off end of value or set value to NULL if it empty 
  469.  *    o return 0 since we found something besides "desc"
  470.  *
  471.  * Sets *KEYP and *VALUEP to point to storage managed by the getrcskey
  472.  * function; the contents are only valid until the next call to getrcskey
  473.  * or getrcsrev.
  474.  */
  475.  
  476. static char *key = NULL;
  477. static char *value = NULL;
  478. static size_t keysize = 0;
  479. static size_t valsize = 0;
  480.  
  481. #define ALLOCINCR 1024
  482.  
  483. static int
  484. getrcskey (fp, keyp, valp)
  485.     FILE *fp;
  486.     char **keyp;
  487.     char **valp;
  488. {
  489.     char *cur, *max;
  490.     int c;
  491.  
  492.     /* skip leading whitespace */
  493.     do
  494.     {
  495.     c = getc (fp);
  496.     if (c == EOF)
  497.     {
  498.         *keyp = (char *) NULL;
  499.         *valp = (char *) NULL;
  500.         return (-1);
  501.     }
  502.     } while (whitespace (c));
  503.  
  504.     /* fill in key */
  505.     cur = key;
  506.     max = key + keysize;
  507.     while (!whitespace (c) && c != ';')
  508.     {
  509.     if (cur >= max)
  510.     {
  511.         key = xrealloc (key, keysize + ALLOCINCR);
  512.         cur = key + keysize;
  513.         keysize += ALLOCINCR;
  514.         max = key + keysize;
  515.     }
  516.     *cur++ = c;
  517.  
  518.     c = getc (fp);
  519.     if (c == EOF)
  520.     {
  521.         *keyp = (char *) NULL;
  522.         *valp = (char *) NULL;
  523.         return (-1);
  524.     }
  525.     }
  526.     if (cur >= max)
  527.     {
  528.     key = xrealloc (key, keysize + ALLOCINCR);
  529.     cur = key + keysize;
  530.     keysize += ALLOCINCR;
  531.     max = key + keysize;
  532.     }
  533.     *cur = '\0';
  534.  
  535.     /* skip whitespace between key and val */
  536.     while (whitespace (c))
  537.     {
  538.     c = getc (fp);
  539.     if (c == EOF)
  540.     {
  541.         *keyp = (char *) NULL;
  542.         *valp = (char *) NULL;
  543.         return (-1);
  544.     }
  545.     } 
  546.  
  547.     /* if we ended key with a semicolon, there is no value */
  548.     if (c == ';')
  549.     {
  550.     *keyp = key;
  551.     *valp = (char *) NULL;
  552.     return (0);
  553.     }
  554.  
  555.     /* otherwise, there might be a value, so fill it in */
  556.     cur = value;
  557.     max = value + valsize;
  558.  
  559.     /* process the value */
  560.     for (;;)
  561.     {
  562.     /* handle RCS "strings" */
  563.     if (c == '@') 
  564.     {
  565.         for (;;)
  566.         {
  567.         c = getc (fp);
  568.         if (c == EOF)
  569.         {
  570.             *keyp = (char *) NULL;
  571.             *valp = (char *) NULL;
  572.             return (-1);
  573.         }
  574.  
  575.         if (c == '@')
  576.         {
  577.             c = getc (fp);
  578.             if (c == EOF)
  579.             {
  580.             *keyp = (char *) NULL;
  581.             *valp = (char *) NULL;
  582.             return (-1);
  583.             }
  584.             
  585.             if (c != '@')
  586.             break;
  587.         }
  588.  
  589.         if (cur >= max)
  590.         {
  591.             value = xrealloc (value, valsize + ALLOCINCR);
  592.             cur = value + valsize;
  593.             valsize += ALLOCINCR;
  594.             max = value + valsize;
  595.         }
  596.         *cur++ = c;
  597.         }
  598.     }
  599.  
  600.     /* The syntax for some key-value pairs is different; they
  601.        don't end with a semicolon.  */
  602.     if (strcmp (key, RCSDESC) == 0
  603.         || strcmp (key, "text") == 0
  604.         || strcmp (key, "log") == 0)
  605.         break;
  606.  
  607.     /* compress whitespace down to a single space */
  608.     if (whitespace (c))
  609.     {
  610.         do {
  611.         c = getc (fp);
  612.         if (c == EOF)
  613.         {
  614.             *keyp = (char *) NULL;
  615.             *valp = (char *) NULL;
  616.             return (-1);
  617.         }
  618.         } while (whitespace (c));
  619.  
  620.         if (cur >= max)
  621.         {
  622.         value = xrealloc (value, valsize + ALLOCINCR);
  623.         cur = value + valsize;
  624.         valsize += ALLOCINCR;
  625.         max = value + valsize;
  626.         }
  627.         *cur++ = ' ';
  628.     }
  629.  
  630.     /* if we got a semi-colon we are done with the entire value */
  631.     if (c == ';')
  632.         break;
  633.  
  634.     if (cur >= max)
  635.     {
  636.         value = xrealloc (value, valsize + ALLOCINCR);
  637.         cur = value + valsize;
  638.         valsize += ALLOCINCR;
  639.         max = value + valsize;
  640.     }
  641.     *cur++ = c;
  642.  
  643.     c = getc (fp);
  644.     if (c == EOF)
  645.     {
  646.         *keyp = (char *) NULL;
  647.         *valp = (char *) NULL;
  648.         return (-1);
  649.     }
  650.     }
  651.  
  652.     /* terminate the string */
  653.     if (cur >= max)
  654.     {
  655.     value = xrealloc (value, valsize + ALLOCINCR);
  656.     cur = value + valsize;
  657.     valsize += ALLOCINCR;
  658.     max = value + valsize;
  659.     }
  660.     *cur = '\0';
  661.  
  662.     /* if the string is empty, make it null */
  663.     if (value && *value != '\0')
  664.     *valp = value;
  665.     else
  666.     *valp = NULL;
  667.     *keyp = key;
  668.     return (0);
  669. }
  670.  
  671. static void getrcsrev PROTO ((FILE *fp, char **revp));
  672.  
  673. /* Read an RCS revision number from FP.  Put a pointer to it in *REVP;
  674.    it points to space managed by getrcsrev which is only good until
  675.    the next call to getrcskey or getrcsrev.  */
  676. static void
  677. getrcsrev (fp, revp)
  678.     FILE *fp;
  679.     char **revp;
  680. {
  681.     char *cur;
  682.     char *max;
  683.     int c;
  684.  
  685.     do {
  686.     c = getc (fp);
  687.     if (c == EOF)
  688.         /* FIXME: should be including filename in error message.  */
  689.         error (1, errno, "cannot read rcs file");
  690.     } while (whitespace (c));
  691.  
  692.     if (!(isdigit (c) || c == '.'))
  693.     /* FIXME: should be including filename in error message.  */
  694.     error (1, 0, "error reading rcs file; revision number expected");
  695.  
  696.     cur = key;
  697.     max = key + keysize;
  698.     while (isdigit (c) || c == '.')
  699.     {
  700.     if (cur >= max)
  701.     {
  702.         key = xrealloc (key, keysize + ALLOCINCR);
  703.         cur = key + keysize;
  704.         keysize += ALLOCINCR;
  705.         max = key + keysize;
  706.     }
  707.     *cur++ = c;
  708.  
  709.     c = getc (fp);
  710.     if (c == EOF)
  711.     {
  712.         /* FIXME: should be including filename in error message.  */
  713.         error (1, errno, "cannot read rcs file");
  714.     }
  715.     }
  716.  
  717.     if (cur >= max)
  718.     {
  719.     key = xrealloc (key, keysize + ALLOCINCR);
  720.     cur = key + keysize;
  721.     keysize += ALLOCINCR;
  722.     max = key + keysize;
  723.     }
  724.     *cur = '\0';
  725.     *revp = key;
  726. }
  727.  
  728. /*
  729.  * process the symbols list of the rcs file
  730.  */
  731. static void
  732. do_symbols (list, val)
  733.     List *list;
  734.     char *val;
  735. {
  736.     Node *p;
  737.     char *cp = val;
  738.     char *tag, *rev;
  739.  
  740.     for (;;)
  741.     {
  742.     /* skip leading whitespace */
  743.     while (whitespace (*cp))
  744.         cp++;
  745.  
  746.     /* if we got to the end, we are done */
  747.     if (*cp == '\0')
  748.         break;
  749.  
  750.     /* split it up into tag and rev */
  751.     tag = cp;
  752.     cp = strchr (cp, ':');
  753.     *cp++ = '\0';
  754.     rev = cp;
  755.     while (!whitespace (*cp) && *cp != '\0')
  756.         cp++;
  757.     if (*cp != '\0')
  758.         *cp++ = '\0';
  759.  
  760.     /* make a new node and add it to the list */
  761.     p = getnode ();
  762.     p->key = xstrdup (tag);
  763.     p->data = xstrdup (rev);
  764.     (void) addnode (list, p);
  765.     }
  766. }
  767.  
  768. /*
  769.  * process the branches list of a revision delta
  770.  */
  771. static void
  772. do_branches (list, val)
  773.     List *list;
  774.     char *val;
  775. {
  776.     Node *p;
  777.     char *cp = val;
  778.     char *branch;
  779.  
  780.     for (;;)
  781.     {
  782.     /* skip leading whitespace */
  783.     while (whitespace (*cp))
  784.         cp++;
  785.  
  786.     /* if we got to the end, we are done */
  787.     if (*cp == '\0')
  788.         break;
  789.  
  790.     /* find the end of this branch */
  791.     branch = cp;
  792.     while (!whitespace (*cp) && *cp != '\0')
  793.         cp++;
  794.     if (*cp != '\0')
  795.         *cp++ = '\0';
  796.  
  797.     /* make a new node and add it to the list */
  798.     p = getnode ();
  799.     p->key = xstrdup (branch);
  800.     (void) addnode (list, p);
  801.     }
  802. }
  803.  
  804. /*
  805.  * Version Number
  806.  * 
  807.  * Returns the requested version number of the RCS file, satisfying tags and/or
  808.  * dates, and walking branches, if necessary.
  809.  * 
  810.  * The result is returned; null-string if error.
  811.  */
  812. char *
  813. RCS_getversion (rcs, tag, date, force_tag_match, return_both)
  814.     RCSNode *rcs;
  815.     char *tag;
  816.     char *date;
  817.     int force_tag_match;
  818.     int return_both;
  819. {
  820.     /* make sure we have something to look at... */
  821.     assert (rcs != NULL);
  822.  
  823.     if (tag && date)
  824.     {
  825.     char *cp, *rev, *tagrev;
  826.  
  827.     /*
  828.      * first lookup the tag; if that works, turn the revision into
  829.      * a branch and lookup the date.
  830.      */
  831.     tagrev = RCS_gettag (rcs, tag, force_tag_match, 0);
  832.     if (tagrev == NULL)
  833.         return ((char *) NULL);
  834.  
  835.     if ((cp = strrchr (tagrev, '.')) != NULL)
  836.         *cp = '\0';
  837.     rev = RCS_getdatebranch (rcs, date, tagrev);
  838.     free (tagrev);
  839.     return (rev);
  840.     }
  841.     else if (tag)
  842.     return (RCS_gettag (rcs, tag, force_tag_match, return_both));
  843.     else if (date)
  844.     return (RCS_getdate (rcs, date, force_tag_match));
  845.     else
  846.     return (RCS_head (rcs));
  847.  
  848. }
  849.  
  850. /*
  851.  * Find the revision for a specific tag.
  852.  * If force_tag_match is set, return NULL if an exact match is not
  853.  * possible otherwise return RCS_head ().  We are careful to look for
  854.  * and handle "magic" revisions specially.
  855.  * 
  856.  * If the matched tag is a branch tag, find the head of the branch.
  857.  */
  858. char *
  859. RCS_gettag (rcs, symtag, force_tag_match, return_both)
  860.     RCSNode *rcs;
  861.     char *symtag;
  862.     int force_tag_match;
  863.     int return_both;
  864. {
  865.     Node *p;
  866.     char *tag = symtag;
  867.  
  868.     /* make sure we have something to look at... */
  869.     assert (rcs != NULL);
  870.  
  871.     /* XXX this is probably not necessary, --jtc */
  872.     if (rcs->flags & PARTIAL) 
  873.     RCS_reparsercsfile (rcs, NULL);
  874.  
  875.     /* If tag is "HEAD", special case to get head RCS revision */
  876.     if (tag && (strcmp (tag, TAG_HEAD) == 0 || *tag == '\0'))
  877. #if 0 /* This #if 0 is only in the Cygnus code.  Why?  Death support?  */
  878.     if (force_tag_match && (rcs->flags & VALID) && (rcs->flags & INATTIC))
  879.         return ((char *) NULL);    /* head request for removed file */
  880.     else
  881. #endif
  882.         return (RCS_head (rcs));
  883.  
  884.     if (!isdigit (tag[0]))
  885.     {
  886.     /* If we got a symbolic tag, resolve it to a numeric */
  887.     if (rcs == NULL)
  888.         p = NULL;
  889.     else {
  890.         p = findnode (RCS_symbols(rcs), tag);
  891.     }
  892.     if (p != NULL)
  893.     {
  894.         int dots;
  895.         char *magic, *branch, *cp;
  896.  
  897.         tag = p->data;
  898.  
  899.         /*
  900.          * If this is a magic revision, we turn it into either its
  901.          * physical branch equivalent (if one exists) or into
  902.          * its base revision, which we assume exists.
  903.          */
  904.         dots = numdots (tag);
  905.         if (dots > 2 && (dots & 1) != 0)
  906.         {
  907.         branch = strrchr (tag, '.');
  908.         cp = branch++ - 1;
  909.         while (*cp != '.')
  910.             cp--;
  911.  
  912.         /* see if we have .magic-branch. (".0.") */
  913.         magic = xmalloc (strlen (tag) + 1);
  914.         (void) sprintf (magic, ".%d.", RCS_MAGIC_BRANCH);
  915.         if (strncmp (magic, cp, strlen (magic)) == 0)
  916.         {
  917.             char *xtag;
  918.  
  919.             /* it's magic.  See if the branch exists */
  920.             *cp = '\0';        /* turn it into a revision */
  921.             xtag = xstrdup (tag);
  922.             *cp = '.';        /* and back again */
  923.             (void) sprintf (magic, "%s.%s", xtag, branch);
  924.             branch = RCS_getbranch (rcs, magic, 1);
  925.             free (magic);
  926.             if (branch != NULL)
  927.             {
  928.             free (xtag);
  929.             return (branch);
  930.             }
  931.             return (xtag);
  932.         }
  933.         free (magic);
  934.         }
  935.     }
  936.     else
  937.     {
  938.         /* The tag wasn't there, so return the head or NULL */
  939.         if (force_tag_match)
  940.         return (NULL);
  941.         else
  942.         return (RCS_head (rcs));
  943.     }
  944.     }
  945.  
  946.     /*
  947.      * numeric tag processing:
  948.      *        1) revision number - just return it
  949.      *        2) branch number   - find head of branch
  950.      */
  951.  
  952.     /* strip trailing dots */
  953.     while (tag[strlen (tag) - 1] == '.')
  954.     tag[strlen (tag) - 1] = '\0';
  955.  
  956.     if ((numdots (tag) & 1) == 0)
  957.     {
  958.     /* we have a branch tag, so we need to walk the branch */
  959.     return (RCS_getbranch (rcs, tag, force_tag_match));
  960.     }
  961.     else
  962.     {
  963.     /* we have a revision tag, so make sure it exists */
  964.     if (rcs == NULL)
  965.         p = NULL;
  966.     else
  967.         p = findnode (rcs->versions, tag);
  968.     if (p != NULL)
  969.     {
  970.         /*
  971.          * we have found a numeric revision for the revision tag.
  972.          * To support expanding the RCS keyword Name, return both
  973.          * the numeric tag and the supplied tag (which might be
  974.          * symbolic).  They are separated with a ':' which is not
  975.          * a valid tag char.  The variable return_both is only set
  976.          * if this function is called through Version_TS ->
  977.          * RCS_getversion.
  978.          */
  979.         if (return_both)
  980.         {
  981.         char *both = xmalloc(strlen(tag) + 2 + strlen(symtag));
  982.         sprintf(both, "%s:%s", tag, symtag);
  983.         return both;
  984.         }
  985.         else
  986.         return (xstrdup (tag));
  987.     }
  988.     else
  989.     {
  990.         /* The revision wasn't there, so return the head or NULL */
  991.         if (force_tag_match)
  992.         return (NULL);
  993.         else
  994.         return (RCS_head (rcs));
  995.     }
  996.     }
  997. }
  998.  
  999. /*
  1000.  * Return a "magic" revision as a virtual branch off of REV for the RCS file.
  1001.  * A "magic" revision is one which is unique in the RCS file.  By unique, I
  1002.  * mean we return a revision which:
  1003.  *    - has a branch of 0 (see rcs.h RCS_MAGIC_BRANCH)
  1004.  *    - has a revision component which is not an existing branch off REV
  1005.  *    - has a revision component which is not an existing magic revision
  1006.  *    - is an even-numbered revision, to avoid conflicts with vendor branches
  1007.  * The first point is what makes it "magic".
  1008.  *
  1009.  * As an example, if we pass in 1.37 as REV, we will look for an existing
  1010.  * branch called 1.37.2.  If it did not exist, we would look for an
  1011.  * existing symbolic tag with a numeric part equal to 1.37.0.2.  If that
  1012.  * didn't exist, then we know that the 1.37.2 branch can be reserved by
  1013.  * creating a symbolic tag with 1.37.0.2 as the numeric part.
  1014.  *
  1015.  * This allows us to fork development with very little overhead -- just a
  1016.  * symbolic tag is used in the RCS file.  When a commit is done, a physical
  1017.  * branch is dynamically created to hold the new revision.
  1018.  *
  1019.  * Note: We assume that REV is an RCS revision and not a branch number.
  1020.  */
  1021. static char *check_rev;
  1022. char *
  1023. RCS_magicrev (rcs, rev)
  1024.     RCSNode *rcs;
  1025.     char *rev;
  1026. {
  1027.     int rev_num;
  1028.     char *xrev, *test_branch;
  1029.  
  1030.     xrev = xmalloc (strlen (rev) + 14); /* enough for .0.number */
  1031.     check_rev = xrev;
  1032.  
  1033.     /* only look at even numbered branches */
  1034.     for (rev_num = 2; ; rev_num += 2)
  1035.     {
  1036.     /* see if the physical branch exists */
  1037.     (void) sprintf (xrev, "%s.%d", rev, rev_num);
  1038.     test_branch = RCS_getbranch (rcs, xrev, 1);
  1039.     if (test_branch != NULL)    /* it did, so keep looking */
  1040.     {
  1041.         free (test_branch);
  1042.         continue;
  1043.     }
  1044.  
  1045.     /* now, create a "magic" revision */
  1046.     (void) sprintf (xrev, "%s.%d.%d", rev, RCS_MAGIC_BRANCH, rev_num);
  1047.  
  1048.     /* walk the symbols list to see if a magic one already exists */
  1049.     if (walklist (RCS_symbols(rcs), checkmagic_proc, NULL) != 0)
  1050.         continue;
  1051.  
  1052.     /* we found a free magic branch.  Claim it as ours */
  1053.     return (xrev);
  1054.     }
  1055. }
  1056.  
  1057. /*
  1058.  * walklist proc to look for a match in the symbols list.
  1059.  * Returns 0 if the symbol does not match, 1 if it does.
  1060.  */
  1061. static int
  1062. checkmagic_proc (p, closure)
  1063.     Node *p;
  1064.     void *closure;
  1065. {
  1066.     if (strcmp (check_rev, p->data) == 0)
  1067.     return (1);
  1068.     else
  1069.     return (0);
  1070. }
  1071.  
  1072. /*
  1073.  * Given an RCSNode, returns non-zero if the specified revision number 
  1074.  * or symbolic tag resolves to a "branch" within the rcs file.
  1075.  *
  1076.  * FIXME: this is the same as RCS_nodeisbranch except for the special 
  1077.  *        case for handling a null rcsnode.
  1078.  */
  1079. int
  1080. RCS_isbranch (rcs, rev)
  1081.     RCSNode *rcs;
  1082.     const char *rev;
  1083. {
  1084.     /* numeric revisions are easy -- even number of dots is a branch */
  1085.     if (isdigit (*rev))
  1086.     return ((numdots (rev) & 1) == 0);
  1087.  
  1088.     /* assume a revision if you can't find the RCS info */
  1089.     if (rcs == NULL)
  1090.     return (0);
  1091.  
  1092.     /* now, look for a match in the symbols list */
  1093.     return (RCS_nodeisbranch (rcs, rev));
  1094. }
  1095.  
  1096. /*
  1097.  * Given an RCSNode, returns non-zero if the specified revision number
  1098.  * or symbolic tag resolves to a "branch" within the rcs file.  We do
  1099.  * take into account any magic branches as well.
  1100.  */
  1101. int
  1102. RCS_nodeisbranch (rcs, rev)
  1103.     RCSNode *rcs;
  1104.     const char *rev;
  1105. {
  1106.     int dots;
  1107.     Node *p;
  1108.  
  1109.     /* numeric revisions are easy -- even number of dots is a branch */
  1110.     if (isdigit (*rev))
  1111.     return ((numdots (rev) & 1) == 0);
  1112.  
  1113.     p = findnode (RCS_symbols(rcs), rev);
  1114.     if (p == NULL)
  1115.     return (0);
  1116.     dots = numdots (p->data);
  1117.     if ((dots & 1) == 0)
  1118.     return (1);
  1119.  
  1120.     /* got a symbolic tag match, but it's not a branch; see if it's magic */
  1121.     if (dots > 2)
  1122.     {
  1123.     char *magic;
  1124.     char *branch = strrchr (p->data, '.');
  1125.     char *cp = branch - 1;
  1126.     while (*cp != '.')
  1127.         cp--;
  1128.  
  1129.     /* see if we have .magic-branch. (".0.") */
  1130.     magic = xmalloc (strlen (p->data) + 1);
  1131.     (void) sprintf (magic, ".%d.", RCS_MAGIC_BRANCH);
  1132.     if (strncmp (magic, cp, strlen (magic)) == 0)
  1133.     {
  1134.         free (magic);
  1135.         return (1);
  1136.     }
  1137.     free (magic);
  1138.     }
  1139.     return (0);
  1140. }
  1141.  
  1142. /*
  1143.  * Returns a pointer to malloc'ed memory which contains the branch
  1144.  * for the specified *symbolic* tag.  Magic branches are handled correctly.
  1145.  */
  1146. char *
  1147. RCS_whatbranch (rcs, rev)
  1148.     RCSNode *rcs;
  1149.     const char *rev;
  1150. {
  1151.     Node *p;
  1152.     int dots;
  1153.  
  1154.     /* assume no branch if you can't find the RCS info */
  1155.     if (rcs == NULL)
  1156.     return ((char *) NULL);
  1157.  
  1158.     /* now, look for a match in the symbols list */
  1159.     p = findnode (RCS_symbols(rcs), rev);
  1160.     if (p == NULL)
  1161.     return ((char *) NULL);
  1162.     dots = numdots (p->data);
  1163.     if ((dots & 1) == 0)
  1164.     return (xstrdup (p->data));
  1165.  
  1166.     /* got a symbolic tag match, but it's not a branch; see if it's magic */
  1167.     if (dots > 2)
  1168.     {
  1169.     char *magic;
  1170.     char *branch = strrchr (p->data, '.');
  1171.     char *cp = branch++ - 1;
  1172.     while (*cp != '.')
  1173.         cp--;
  1174.  
  1175.     /* see if we have .magic-branch. (".0.") */
  1176.     magic = xmalloc (strlen (p->data) + 1);
  1177.     (void) sprintf (magic, ".%d.", RCS_MAGIC_BRANCH);
  1178.     if (strncmp (magic, cp, strlen (magic)) == 0)
  1179.     {
  1180.         /* yep.  it's magic.  now, construct the real branch */
  1181.         *cp = '\0';            /* turn it into a revision */
  1182.         (void) sprintf (magic, "%s.%s", p->data, branch);
  1183.         *cp = '.';            /* and turn it back */
  1184.         return (magic);
  1185.     }
  1186.     free (magic);
  1187.     }
  1188.     return ((char *) NULL);
  1189. }
  1190.  
  1191. /*
  1192.  * Get the head of the specified branch.  If the branch does not exist,
  1193.  * return NULL or RCS_head depending on force_tag_match
  1194.  */
  1195. char *
  1196. RCS_getbranch (rcs, tag, force_tag_match)
  1197.     RCSNode *rcs;
  1198.     char *tag;
  1199.     int force_tag_match;
  1200. {
  1201.     Node *p, *head;
  1202.     RCSVers *vn;
  1203.     char *xtag;
  1204.     char *nextvers;
  1205.     char *cp;
  1206.  
  1207.     /* make sure we have something to look at... */
  1208.     assert (rcs != NULL);
  1209.  
  1210.     if (rcs->flags & PARTIAL)
  1211.     RCS_reparsercsfile (rcs, NULL);
  1212.  
  1213.     /* find out if the tag contains a dot, or is on the trunk */
  1214.     cp = strrchr (tag, '.');
  1215.  
  1216.     /* trunk processing is the special case */
  1217.     if (cp == NULL)
  1218.     {
  1219.     xtag = xmalloc (strlen (tag) + 1 + 1);    /* +1 for an extra . */
  1220.     (void) strcpy (xtag, tag);
  1221.     (void) strcat (xtag, ".");
  1222.     for (cp = rcs->head; cp != NULL;)
  1223.     {
  1224.         if (strncmp (xtag, cp, strlen (xtag)) == 0)
  1225.         break;
  1226.         p = findnode (rcs->versions, cp);
  1227.         if (p == NULL)
  1228.         {
  1229.         free (xtag);
  1230.         if (force_tag_match)
  1231.             return (NULL);
  1232.         else
  1233.             return (RCS_head (rcs));
  1234.         }
  1235.         vn = (RCSVers *) p->data;
  1236.         cp = vn->next;
  1237.     }
  1238.     free (xtag);
  1239.     if (cp == NULL)
  1240.     {
  1241.         if (force_tag_match)
  1242.         return (NULL);
  1243.         else
  1244.         return (RCS_head (rcs));
  1245.     }
  1246.     return (xstrdup (cp));
  1247.     }
  1248.  
  1249.     /* if it had a `.', terminate the string so we have the base revision */
  1250.     *cp = '\0';
  1251.  
  1252.     /* look up the revision this branch is based on */
  1253.     p = findnode (rcs->versions, tag);
  1254.  
  1255.     /* put the . back so we have the branch again */
  1256.     *cp = '.';
  1257.  
  1258.     if (p == NULL)
  1259.     {
  1260.     /* if the base revision didn't exist, return head or NULL */
  1261.     if (force_tag_match)
  1262.         return (NULL);
  1263.     else
  1264.         return (RCS_head (rcs));
  1265.     }
  1266.  
  1267.     /* find the first element of the branch we are looking for */
  1268.     vn = (RCSVers *) p->data;
  1269.     if (vn->branches == NULL)
  1270.     return (NULL);
  1271.     xtag = xmalloc (strlen (tag) + 1 + 1);    /* 1 for the extra '.' */
  1272.     (void) strcpy (xtag, tag);
  1273.     (void) strcat (xtag, ".");
  1274.     head = vn->branches->list;
  1275.     for (p = head->next; p != head; p = p->next)
  1276.     if (strncmp (p->key, xtag, strlen (xtag)) == 0)
  1277.         break;
  1278.     free (xtag);
  1279.  
  1280.     if (p == head)
  1281.     {
  1282.     /* we didn't find a match so return head or NULL */
  1283.     if (force_tag_match)
  1284.         return (NULL);
  1285.     else
  1286.         return (RCS_head (rcs));
  1287.     }
  1288.  
  1289.     /* now walk the next pointers of the branch */
  1290.     nextvers = p->key;
  1291.     do
  1292.     {
  1293.     p = findnode (rcs->versions, nextvers);
  1294.     if (p == NULL)
  1295.     {
  1296.         /* a link in the chain is missing - return head or NULL */
  1297.         if (force_tag_match)
  1298.         return (NULL);
  1299.         else
  1300.         return (RCS_head (rcs));
  1301.     }
  1302.     vn = (RCSVers *) p->data;
  1303.     nextvers = vn->next;
  1304.     } while (nextvers != NULL);
  1305.  
  1306.     /* we have the version in our hand, so go for it */
  1307.     return (xstrdup (vn->version));
  1308. }
  1309.  
  1310. /*
  1311.  * Get the head of the RCS file.  If branch is set, this is the head of the
  1312.  * branch, otherwise the real head
  1313.  */
  1314. char *
  1315. RCS_head (rcs)
  1316.     RCSNode *rcs;
  1317. {
  1318.     /* make sure we have something to look at... */
  1319.     assert (rcs != NULL);
  1320.  
  1321.     /*
  1322.      * NOTE: we call getbranch with force_tag_match set to avoid any
  1323.      * possibility of recursion
  1324.      */
  1325.     if (rcs->branch)
  1326.     return (RCS_getbranch (rcs, rcs->branch, 1));
  1327.     else
  1328.     return (xstrdup (rcs->head));
  1329. }
  1330.  
  1331. /*
  1332.  * Get the most recent revision, based on the supplied date, but use some
  1333.  * funky stuff and follow the vendor branch maybe
  1334.  */
  1335. char *
  1336. RCS_getdate (rcs, date, force_tag_match)
  1337.     RCSNode *rcs;
  1338.     char *date;
  1339.     int force_tag_match;
  1340. {
  1341.     char *cur_rev = NULL;
  1342.     char *retval = NULL;
  1343.     Node *p;
  1344.     RCSVers *vers = NULL;
  1345.  
  1346.     /* make sure we have something to look at... */
  1347.     assert (rcs != NULL);
  1348.  
  1349.     if (rcs->flags & PARTIAL)
  1350.     RCS_reparsercsfile (rcs, NULL);
  1351.  
  1352.     /* if the head is on a branch, try the branch first */
  1353.     if (rcs->branch != NULL)
  1354.     retval = RCS_getdatebranch (rcs, date, rcs->branch);
  1355.  
  1356.     /* if we found a match, we are done */
  1357.     if (retval != NULL)
  1358.     return (retval);
  1359.  
  1360.     /* otherwise if we have a trunk, try it */
  1361.     if (rcs->head)
  1362.     {
  1363.     p = findnode (rcs->versions, rcs->head);
  1364.     while (p != NULL)
  1365.     {
  1366.         /* if the date of this one is before date, take it */
  1367.         vers = (RCSVers *) p->data;
  1368.         if (RCS_datecmp (vers->date, date) <= 0)
  1369.         {
  1370.         cur_rev = vers->version;
  1371.         break;
  1372.         }
  1373.  
  1374.         /* if there is a next version, find the node */
  1375.         if (vers->next != NULL)
  1376.         p = findnode (rcs->versions, vers->next);
  1377.         else
  1378.         p = (Node *) NULL;
  1379.     }
  1380.     }
  1381.  
  1382.     /*
  1383.      * at this point, either we have the revision we want, or we have the
  1384.      * first revision on the trunk (1.1?) in our hands
  1385.      */
  1386.  
  1387.     /* if we found what we're looking for, and it's not 1.1 return it */
  1388.     if (cur_rev != NULL && strcmp (cur_rev, "1.1") != 0)
  1389.     return (xstrdup (cur_rev));
  1390.  
  1391.     /* look on the vendor branch */
  1392.     retval = RCS_getdatebranch (rcs, date, CVSBRANCH);
  1393.  
  1394.     /*
  1395.      * if we found a match, return it; otherwise, we return the first
  1396.      * revision on the trunk or NULL depending on force_tag_match and the
  1397.      * date of the first rev
  1398.      */
  1399.     if (retval != NULL)
  1400.     return (retval);
  1401.  
  1402.     if (!force_tag_match || RCS_datecmp (vers->date, date) <= 0)
  1403.     return (xstrdup (vers->version));
  1404.     else
  1405.     return (NULL);
  1406. }
  1407.  
  1408. /*
  1409.  * Look up the last element on a branch that was put in before the specified
  1410.  * date (return the rev or NULL)
  1411.  */
  1412. static char *
  1413. RCS_getdatebranch (rcs, date, branch)
  1414.     RCSNode *rcs;
  1415.     char *date;
  1416.     char *branch;
  1417. {
  1418.     char *cur_rev = NULL;
  1419.     char *cp;
  1420.     char *xbranch, *xrev;
  1421.     Node *p;
  1422.     RCSVers *vers;
  1423.  
  1424.     /* look up the first revision on the branch */
  1425.     xrev = xstrdup (branch);
  1426.     cp = strrchr (xrev, '.');
  1427.     if (cp == NULL)
  1428.     {
  1429.     free (xrev);
  1430.     return (NULL);
  1431.     }
  1432.     *cp = '\0';                /* turn it into a revision */
  1433.  
  1434.     assert (rcs != NULL);
  1435.  
  1436.     if (rcs->flags & PARTIAL)
  1437.     RCS_reparsercsfile (rcs, NULL);
  1438.  
  1439.     p = findnode (rcs->versions, xrev);
  1440.     free (xrev);
  1441.     if (p == NULL)
  1442.     return (NULL);
  1443.     vers = (RCSVers *) p->data;
  1444.  
  1445.     /* if no branches list, return NULL */
  1446.     if (vers->branches == NULL)
  1447.     return (NULL);
  1448.  
  1449.     /* walk the branches list looking for the branch number */
  1450.     xbranch = xmalloc (strlen (branch) + 1 + 1); /* +1 for the extra dot */
  1451.     (void) strcpy (xbranch, branch);
  1452.     (void) strcat (xbranch, ".");
  1453.     for (p = vers->branches->list->next; p != vers->branches->list; p = p->next)
  1454.     if (strncmp (p->key, xbranch, strlen (xbranch)) == 0)
  1455.         break;
  1456.     free (xbranch);
  1457.     if (p == vers->branches->list)
  1458.     return (NULL);
  1459.  
  1460.     p = findnode (rcs->versions, p->key);
  1461.  
  1462.     /* walk the next pointers until you find the end, or the date is too late */
  1463.     while (p != NULL)
  1464.     {
  1465.     vers = (RCSVers *) p->data;
  1466.     if (RCS_datecmp (vers->date, date) <= 0)
  1467.         cur_rev = vers->version;
  1468.     else
  1469.         break;
  1470.  
  1471.     /* if there is a next version, find the node */
  1472.     if (vers->next != NULL)
  1473.         p = findnode (rcs->versions, vers->next);
  1474.     else
  1475.         p = (Node *) NULL;
  1476.     }
  1477.  
  1478.     /* if we found something acceptable, return it - otherwise NULL */
  1479.     if (cur_rev != NULL)
  1480.     return (xstrdup (cur_rev));
  1481.     else
  1482.     return (NULL);
  1483. }
  1484.  
  1485. /*
  1486.  * Compare two dates in RCS format. Beware the change in format on January 1,
  1487.  * 2000, when years go from 2-digit to full format.
  1488.  */
  1489. int
  1490. RCS_datecmp (date1, date2)
  1491.     char *date1, *date2;
  1492. {
  1493.     int length_diff = strlen (date1) - strlen (date2);
  1494.  
  1495.     return (length_diff ? length_diff : strcmp (date1, date2));
  1496. }
  1497.  
  1498. /*
  1499.  * Lookup the specified revision in the ,v file and return, in the date
  1500.  * argument, the date specified for the revision *minus one second*, so that
  1501.  * the logically previous revision will be found later.
  1502.  * 
  1503.  * Returns zero on failure, RCS revision time as a Unix "time_t" on success.
  1504.  */
  1505. time_t
  1506. RCS_getrevtime (rcs, rev, date, fudge)
  1507.     RCSNode *rcs;
  1508.     char *rev;
  1509.     char *date;
  1510.     int fudge;
  1511. {
  1512.     char tdate[MAXDATELEN];
  1513.     struct tm xtm, *ftm;
  1514.     time_t revdate = 0;
  1515.     Node *p;
  1516.     RCSVers *vers;
  1517.  
  1518.     /* make sure we have something to look at... */
  1519.     assert (rcs != NULL);
  1520.  
  1521.     if (rcs->flags & PARTIAL)
  1522.     RCS_reparsercsfile (rcs, NULL);
  1523.  
  1524.     /* look up the revision */
  1525.     p = findnode (rcs->versions, rev);
  1526.     if (p == NULL)
  1527.     return (-1);
  1528.     vers = (RCSVers *) p->data;
  1529.  
  1530.     /* split up the date */
  1531.     ftm = &xtm;
  1532.     (void) sscanf (vers->date, SDATEFORM, &ftm->tm_year, &ftm->tm_mon,
  1533.            &ftm->tm_mday, &ftm->tm_hour, &ftm->tm_min,
  1534.            &ftm->tm_sec);
  1535.  
  1536.     /* If the year is from 1900 to 1999, RCS files contain only two
  1537.        digits, and sscanf gives us a year from 0-99.  If the year is
  1538.        2000+, RCS files contain all four digits and we subtract 1900,
  1539.        because the tm_year field should contain years since 1900.  */
  1540.  
  1541.     if (ftm->tm_year > 1900)
  1542.     ftm->tm_year -= 1900;
  1543.  
  1544.     /* put the date in a form getdate can grok */
  1545. #ifdef HAVE_RCS5
  1546.     (void) sprintf (tdate, "%d/%d/%d GMT %d:%d:%d", ftm->tm_mon,
  1547.             ftm->tm_mday, ftm->tm_year, ftm->tm_hour,
  1548.             ftm->tm_min, ftm->tm_sec);
  1549. #else
  1550.     (void) sprintf (tdate, "%d/%d/%d %d:%d:%d", ftm->tm_mon,
  1551.             ftm->tm_mday, ftm->tm_year, ftm->tm_hour,
  1552.             ftm->tm_min, ftm->tm_sec);
  1553. #endif
  1554.  
  1555.     /* turn it into seconds since the epoch */
  1556.     revdate = get_date (tdate, (struct timeb *) NULL);
  1557.     if (revdate != (time_t) -1)
  1558.     {
  1559.     revdate -= fudge;        /* remove "fudge" seconds */
  1560.     if (date)
  1561.     {
  1562.         /* put an appropriate string into ``date'' if we were given one */
  1563. #ifdef HAVE_RCS5
  1564.         ftm = gmtime (&revdate);
  1565. #else
  1566.         ftm = localtime (&revdate);
  1567. #endif
  1568.         (void) sprintf (date, DATEFORM,
  1569.                 ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
  1570.                 ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
  1571.                 ftm->tm_min, ftm->tm_sec);
  1572.     }
  1573.     }
  1574.     return (revdate);
  1575. }
  1576.  
  1577. List *
  1578. RCS_symbols(rcs)
  1579.     RCSNode *rcs;
  1580. {
  1581.     assert(rcs != NULL);
  1582.  
  1583.     if (rcs->flags & PARTIAL)
  1584.     RCS_reparsercsfile (rcs, NULL);
  1585.  
  1586.     if (rcs->symbols_data) {
  1587.     rcs->symbols = getlist ();
  1588.     do_symbols (rcs->symbols, rcs->symbols_data);
  1589.     free(rcs->symbols_data);
  1590.     rcs->symbols_data = NULL;
  1591.     }
  1592.  
  1593.     return rcs->symbols;
  1594. }
  1595.  
  1596. /*
  1597.  * The argument ARG is the getopt remainder of the -k option specified on the
  1598.  * command line.  This function returns malloc'ed space that can be used
  1599.  * directly in calls to RCS V5, with the -k flag munged correctly.
  1600.  */
  1601. char *
  1602. RCS_check_kflag (arg)
  1603.     const char *arg;
  1604. {
  1605.     static const char *const kflags[] =
  1606.     {"kv", "kvl", "k", "v", "o", "b", (char *) NULL};
  1607.     static const char *const  keyword_usage[] =
  1608.     {
  1609.       "%s %s: invalid RCS keyword expansion mode\n",
  1610.       "Valid expansion modes include:\n",
  1611.       "   -kkv\tGenerate keywords using the default form.\n",
  1612.       "   -kkvl\tLike -kkv, except locker's name inserted.\n",
  1613.       "   -kk\tGenerate only keyword names in keyword strings.\n",
  1614.       "   -kv\tGenerate only keyword values in keyword strings.\n",
  1615.       "   -ko\tGenerate the old keyword string (no changes from checked in file).\n",
  1616.       "   -kb\tGenerate binary file unmodified (merges not allowed) (RCS 5.7).\n",
  1617.       NULL,
  1618.     };
  1619.     char karg[10];
  1620.     char const *const *cpp = NULL;
  1621.  
  1622. #ifndef HAVE_RCS5
  1623.     error (1, 0, "%s %s: your version of RCS does not support the -k option",
  1624.        program_name, command_name);
  1625. #endif
  1626.  
  1627.     if (arg)
  1628.     {
  1629.     for (cpp = kflags; *cpp != NULL; cpp++)
  1630.     {
  1631.         if (strcmp (arg, *cpp) == 0)
  1632.         break;
  1633.     }
  1634.     }
  1635.  
  1636.     if (arg == NULL || *cpp == NULL)
  1637.     {
  1638.     usage (keyword_usage);
  1639.     }
  1640.  
  1641.     (void) sprintf (karg, "-k%s", *cpp);
  1642.     return (xstrdup (karg));
  1643. }
  1644.  
  1645. /*
  1646.  * Do some consistency checks on the symbolic tag... These should equate
  1647.  * pretty close to what RCS checks, though I don't know for certain.
  1648.  */
  1649. void
  1650. RCS_check_tag (tag)
  1651.     const char *tag;
  1652. {
  1653.     char *invalid = "$,.:;@";        /* invalid RCS tag characters */
  1654.     const char *cp;
  1655.  
  1656.     /*
  1657.      * The first character must be an alphabetic letter. The remaining
  1658.      * characters cannot be non-visible graphic characters, and must not be
  1659.      * in the set of "invalid" RCS identifier characters.
  1660.      */
  1661.     if (isalpha (*tag))
  1662.     {
  1663.     for (cp = tag; *cp; cp++)
  1664.     {
  1665.         if (!isgraph (*cp))
  1666.         error (1, 0, "tag `%s' has non-visible graphic characters",
  1667.                tag);
  1668.         if (strchr (invalid, *cp))
  1669.         error (1, 0, "tag `%s' must not contain the characters `%s'",
  1670.                tag, invalid);
  1671.     }
  1672.     }
  1673.     else
  1674.     error (1, 0, "tag `%s' must start with a letter", tag);
  1675. }
  1676.  
  1677. /*
  1678.  * Return true if RCS revision with TAG is a dead revision.
  1679.  */
  1680. int
  1681. RCS_isdead (rcs, tag)
  1682.     RCSNode *rcs;
  1683.     const char *tag;
  1684. {
  1685.     Node *p;
  1686.     RCSVers *version;
  1687.  
  1688.     if (rcs->flags & PARTIAL)
  1689.     RCS_reparsercsfile (rcs, NULL);
  1690.  
  1691.     p = findnode (rcs->versions, tag);
  1692.     if (p == NULL)
  1693.     return (0);
  1694.  
  1695.     version = (RCSVers *) p->data;
  1696.     return (version->dead);
  1697. }
  1698.  
  1699. /* Return the RCS keyword expansion mode.  For example "b" for binary.
  1700.    Returns a pointer into storage which is allocated and freed along with
  1701.    the rest of the RCS information; the caller should not modify this
  1702.    storage.  Returns NULL if the RCS file does not specify a keyword
  1703.    expansion mode; for all other errors, die with a fatal error.  */
  1704. char *
  1705. RCS_getexpand (rcs)
  1706.     RCSNode *rcs;
  1707. {
  1708.     assert (rcs != NULL);
  1709.     if (rcs->flags & PARTIAL)
  1710.     RCS_reparsercsfile (rcs, NULL);
  1711.     return rcs->expand;
  1712. }
  1713.  
  1714. /* Stuff related to annotate command.  This should perhaps be split
  1715.    into the stuff which knows about the guts of RCS files, and the
  1716.    command parsing type stuff.  */
  1717.  
  1718. /* Linked list of allocated blocks.  Seems kind of silly to
  1719.    reinvent the obstack wheel, and this isn't as nice as obstacks
  1720.    in some ways, but obstacks are pretty baroque.  */
  1721. struct allocblock
  1722. {
  1723.     char *text;
  1724.     struct allocblock *next;
  1725. };
  1726. struct allocblock *blocks;
  1727.  
  1728. static void *block_alloc PROTO ((size_t));
  1729.  
  1730. static void *
  1731. block_alloc (n)
  1732.     size_t n;
  1733. {
  1734.     struct allocblock *blk;
  1735.     blk = (struct allocblock *) xmalloc (sizeof (struct allocblock));
  1736.     blk->text = xmalloc (n);
  1737.     blk->next = blocks;
  1738.     blocks = blk;
  1739.     return blk->text;
  1740. }
  1741.  
  1742. static void block_free PROTO ((void));
  1743.  
  1744. static void
  1745. block_free ()
  1746. {
  1747.     struct allocblock *p;
  1748.     struct allocblock *q;
  1749.  
  1750.     p = blocks;
  1751.     while (p != NULL)
  1752.     {
  1753.     free (p->text);
  1754.     q = p->next;
  1755.     free (p);
  1756.     p = q;
  1757.     }
  1758.     blocks = NULL;
  1759. }
  1760.  
  1761. struct line
  1762. {
  1763.     /* Text of this line, terminated by \n or \0.  */
  1764.     char *text;
  1765.     /* Version in which it was introduced.  */
  1766.     RCSVers *vers;
  1767.     /* Nonzero if this line ends with \n.  This will always be true
  1768.        except possibly for the last line.  */
  1769.     int has_newline;
  1770. };
  1771.  
  1772. struct linevector
  1773. {
  1774.     /* How many lines in use for this linevector?  */
  1775.     unsigned int nlines;
  1776.     /* How many lines allocated for this linevector?  */
  1777.     unsigned int lines_alloced;
  1778.     /* Pointer to array containing a pointer to each line.  */
  1779.     struct line **vector;
  1780. };
  1781.  
  1782. static void linevector_init PROTO ((struct linevector *));
  1783.  
  1784. /* Initialize *VEC to be a linevector with no lines.  */
  1785. static void
  1786. linevector_init (vec)
  1787.     struct linevector *vec;
  1788. {
  1789.     vec->lines_alloced = 10;
  1790.     vec->nlines = 0;
  1791.     vec->vector = (struct line **)
  1792.     xmalloc (vec->lines_alloced * sizeof (*vec->vector));
  1793. }
  1794.  
  1795. static void linevector_add PROTO ((struct linevector *vec, char *text,
  1796.                    RCSVers *vers, unsigned int pos));
  1797.  
  1798. /* Given some text TEXT, add each of its lines to VEC before line POS
  1799.    (where line 0 is the first line).  The last line in TEXT may or may
  1800.    not be \n terminated.  All \n in TEXT are changed to \0.  Set the
  1801.    version for each of the new lines to VERS.  */
  1802. static void
  1803. linevector_add (vec, text, vers, pos)
  1804.     struct linevector *vec;
  1805.     char *text;
  1806.     RCSVers *vers;
  1807.     unsigned int pos;
  1808. {
  1809.     unsigned int i;
  1810.     unsigned int nnew;
  1811.     char *p;
  1812.     struct line *lines;
  1813.  
  1814.     assert (vec->lines_alloced > 0);
  1815.  
  1816.     /* Count the number of lines we will need to add.  */
  1817.     nnew = 1;
  1818.     for (p = text; *p != '\0'; ++p)
  1819.     if (*p == '\n' && p[1] != '\0')
  1820.         ++nnew;
  1821.     /* Allocate the struct line's.  */
  1822.     lines = block_alloc (nnew * sizeof (struct line));
  1823.  
  1824.     /* Expand VEC->VECTOR if needed.  */
  1825.     if (vec->nlines + nnew >= vec->lines_alloced)
  1826.     {
  1827.     while (vec->nlines + nnew >= vec->lines_alloced)
  1828.         vec->lines_alloced *= 2;
  1829.     vec->vector = xrealloc (vec->vector,
  1830.                 vec->lines_alloced * sizeof (*vec->vector));
  1831.     }
  1832.  
  1833.     /* Make room for the new lines in VEC->VECTOR.  */
  1834.     for (i = vec->nlines + nnew - 1; i >= pos + nnew; --i)
  1835.     vec->vector[i] = vec->vector[i - nnew];
  1836.  
  1837.     if (pos > vec->nlines)
  1838.     error (1, 0, "invalid rcs file: line to add out of range");
  1839.  
  1840.     /* Actually add the lines, to LINES and VEC->VECTOR.  */
  1841.     i = pos;
  1842.     lines[0].text = text;
  1843.     lines[0].vers = vers;
  1844.     lines[0].has_newline = 0;
  1845.     vec->vector[i++] = &lines[0];
  1846.     for (p = text; *p != '\0'; ++p)
  1847.     if (*p == '\n')
  1848.     {
  1849.         *p = '\0';
  1850.         lines[i - pos - 1].has_newline = 1;
  1851.         if (p[1] == '\0')
  1852.         /* If there are no characters beyond the last newline, we
  1853.            don't consider it another line.  */
  1854.         break;
  1855.         lines[i - pos].text = p + 1;
  1856.         lines[i - pos].vers = vers;
  1857.         lines[i - pos].has_newline = 0;
  1858.         vec->vector[i] = &lines[i - pos];
  1859.         ++i;
  1860.     }
  1861.     vec->nlines += nnew;
  1862. }
  1863.  
  1864. static void linevector_delete PROTO ((struct linevector *, unsigned int,
  1865.                       unsigned int));
  1866.  
  1867. /* Remove NLINES lines from VEC at position POS (where line 0 is the
  1868.    first line).  */
  1869. static void
  1870. linevector_delete (vec, pos, nlines)
  1871.     struct linevector *vec;
  1872.     unsigned int pos;
  1873.     unsigned int nlines;
  1874. {
  1875.     unsigned int i;
  1876.     unsigned int last;
  1877.  
  1878.     last = vec->nlines - nlines;
  1879.     for (i = pos; i < last; ++i)
  1880.     vec->vector[i] = vec->vector[i + nlines];
  1881.     vec->nlines -= nlines;
  1882. }
  1883.  
  1884. static void linevector_copy PROTO ((struct linevector *, struct linevector *));
  1885.  
  1886. /* Copy FROM to TO, copying the vectors but not the lines pointed to.  */
  1887. static void
  1888. linevector_copy (to, from)
  1889.     struct linevector *to;
  1890.     struct linevector *from;
  1891. {
  1892.     if (from->nlines > to->lines_alloced)
  1893.     {
  1894.     while (from->nlines > to->lines_alloced)
  1895.         to->lines_alloced *= 2;
  1896.     to->vector = (struct line **)
  1897.         xrealloc (to->vector, to->lines_alloced * sizeof (*to->vector));
  1898.     }
  1899.     memcpy (to->vector, from->vector,
  1900.         from->nlines * sizeof (*to->vector));
  1901.     to->nlines = from->nlines;
  1902. }
  1903.  
  1904. static void linevector_free PROTO ((struct linevector *));
  1905.  
  1906. /* Free storage associated with linevector (that is, the vector but
  1907.    not the lines pointed to).  */
  1908. static void
  1909. linevector_free (vec)
  1910.     struct linevector *vec;
  1911. {
  1912.    free (vec->vector);
  1913. }
  1914.  
  1915. static char *month_printname PROTO ((char *));
  1916.  
  1917. /* Given a textual string giving the month (1-12), terminated with any
  1918.    character not recognized by atoi, return the 3 character name to
  1919.    print it with.  I do not think it is a good idea to change these
  1920.    strings based on the locale; they are standard abbreviations (for
  1921.    example in rfc822 mail messages) which should be widely understood.
  1922.    Returns a pointer into static readonly storage.  */
  1923. static char *
  1924. month_printname (month)
  1925.     char *month;
  1926. {
  1927.     static const char *const months[] =
  1928.       {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
  1929.      "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
  1930.     int mnum;
  1931.  
  1932.     mnum = atoi (month);
  1933.     if (mnum < 1 || mnum > 12)
  1934.     return "???";
  1935.     return (char *)months[mnum - 1];
  1936. }
  1937.  
  1938. static int annotate_fileproc PROTO ((struct file_info *));
  1939.  
  1940. static int
  1941. annotate_fileproc (finfo)
  1942.     struct file_info *finfo;
  1943. {
  1944.     FILE *fp;
  1945.     char *key;
  1946.     char *value;
  1947.     RCSVers *vers;
  1948.     RCSVers *prev_vers;
  1949.     int n;
  1950.     int ishead;
  1951.     Node *node;
  1952.     struct linevector headlines;
  1953.     struct linevector curlines;
  1954.  
  1955.     if (finfo->rcs == NULL)
  1956.         return (1);
  1957.  
  1958.     /* Distinguish output for various files if we are processing
  1959.        several files.  */
  1960.     cvs_outerr ("Annotations for ", 0);
  1961.     cvs_outerr (finfo->fullname, 0);
  1962.     cvs_outerr ("\n***************\n", 0);
  1963.  
  1964.     if (!(finfo->rcs->flags & PARTIAL))
  1965.     /* We are leaking memory by calling RCS_reparsefile again.  */
  1966.     error (0, 0, "internal warning: non-partial rcs in annotate_fileproc");
  1967.     RCS_reparsercsfile (finfo->rcs, &fp);
  1968.  
  1969.     ishead = 1;
  1970.     vers = NULL;
  1971.  
  1972.     do {
  1973.     getrcsrev (fp, &key);
  1974.  
  1975.     /* Stash the previous version.  */
  1976.     prev_vers = vers;
  1977.  
  1978.     /* look up the revision */
  1979.     node = findnode (finfo->rcs->versions, key);
  1980.     if (node == NULL)
  1981.         error (1, 0, "mismatch in rcs file %s between deltas and deltatexts",
  1982.            finfo->rcs->path);
  1983.     vers = (RCSVers *) node->data;
  1984.  
  1985.     while ((n = getrcskey (fp, &key, &value)) >= 0)
  1986.     {
  1987.         if (strcmp (key, "text") == 0)
  1988.         {
  1989.         if (ishead)
  1990.         {
  1991.             char *p;
  1992.  
  1993.             p = block_alloc (strlen (value) + 1);
  1994.             strcpy (p, value);
  1995.  
  1996.             linevector_init (&headlines);
  1997.             linevector_init (&curlines);
  1998.             linevector_add (&headlines, p, NULL, 0);
  1999.             linevector_copy (&curlines, &headlines);
  2000.             ishead = 0;
  2001.         }
  2002.         else
  2003.         {
  2004.             char *p;
  2005.             char *q;
  2006.             int op;
  2007.             /* The RCS format throws us for a loop in that the
  2008.                deltafrags (if we define a deltafrag as an
  2009.                add or a delete) need to be applied in reverse
  2010.                order.  So we stick them into a linked list.  */
  2011.             struct deltafrag {
  2012.             enum {ADD, DELETE} type;
  2013.             unsigned long pos;
  2014.             unsigned long nlines;
  2015.             char *new_lines;
  2016.             struct deltafrag *next;
  2017.             };
  2018.             struct deltafrag *dfhead;
  2019.             struct deltafrag *df;
  2020.  
  2021.             dfhead = NULL;
  2022.             for (p = value; p != NULL && *p != '\0'; )
  2023.             {
  2024.             op = *p++;
  2025.             if (op != 'a' && op != 'd')
  2026.                 /* Can't just skip over the deltafrag, because
  2027.                    the value of op determines the syntax.  */
  2028.                 error (1, 0, "unrecognized operation '%c' in %s",
  2029.                    op, finfo->rcs->path);
  2030.             df = (struct deltafrag *)
  2031.                 xmalloc (sizeof (struct deltafrag));
  2032.             df->next = dfhead;
  2033.             dfhead = df;
  2034.             df->pos = strtoul (p, &q, 10);
  2035.  
  2036.             if (p == q)
  2037.                 error (1, 0, "number expected in %s",
  2038.                    finfo->rcs->path);
  2039.             p = q;
  2040.             if (*p++ != ' ')
  2041.                 error (1, 0, "space expected in %s",
  2042.                    finfo->rcs->path);
  2043.             df->nlines = strtoul (p, &q, 10);
  2044.             if (p == q)
  2045.                 error (1, 0, "number expected in %s",
  2046.                    finfo->rcs->path);
  2047.             p = q;
  2048.             if (*p++ != '\012')
  2049.                 error (1, 0, "linefeed expected in %s",
  2050.                    finfo->rcs->path);
  2051.  
  2052.             if (op == 'a')
  2053.             {
  2054.                 unsigned int i;
  2055.  
  2056.                 df->type = ADD;
  2057.                 i = df->nlines;
  2058.                 /* The text we want is the number of lines
  2059.                    specified, or until the end of the value,
  2060.                    whichever comes first (it will be the former
  2061.                    except in the case where we are adding a line
  2062.                    which does not end in newline).  */
  2063.                 for (q = p; i != 0; ++q)
  2064.                 if (*q == '\n')
  2065.                     --i;
  2066.                 else if (*q == '\0')
  2067.                 {
  2068.                     if (i != 1)
  2069.                     error (1, 0, "\
  2070. invalid rcs file %s: premature end of value",
  2071.                            finfo->rcs->path);
  2072.                     else
  2073.                     break;
  2074.                 }
  2075.  
  2076.                 /* Copy the text we are adding into allocated
  2077.                    space.  */
  2078.                 df->new_lines = block_alloc (q - p + 1);
  2079.                 strncpy (df->new_lines, p, q - p);
  2080.                 df->new_lines[q - p] = '\0';
  2081.  
  2082.                 p = q;
  2083.             }
  2084.             else
  2085.             {
  2086.                 /* Correct for the fact that line numbers in RCS
  2087.                    files start with 1.  */
  2088.                 --df->pos;
  2089.  
  2090.                 assert (op == 'd');
  2091.                 df->type = DELETE;
  2092.             }
  2093.             }
  2094.             for (df = dfhead; df != NULL;)
  2095.             {
  2096.             unsigned int ln;
  2097.  
  2098.             switch (df->type)
  2099.             {
  2100.             case ADD:
  2101.                 linevector_add (&curlines, df->new_lines,
  2102.                         NULL, df->pos);
  2103.                 break;
  2104.             case DELETE:
  2105.                 if (df->pos > curlines.nlines
  2106.                 || df->pos + df->nlines > curlines.nlines)
  2107.                 error (1, 0, "\
  2108. invalid rcs file %s (`d' operand out of range)",
  2109.                        finfo->rcs->path);
  2110.                 for (ln = df->pos; ln < df->pos + df->nlines; ++ln)
  2111.                 curlines.vector[ln]->vers = prev_vers;
  2112.                 linevector_delete (&curlines, df->pos, df->nlines);
  2113.                 break;
  2114.             }
  2115.                 df = df->next;
  2116.             free (dfhead);
  2117.             dfhead = df;
  2118.             }
  2119.         }
  2120.         break;
  2121.         }
  2122.     }
  2123.     if (n < 0)
  2124.         goto l_error;
  2125.     } while (vers->next != NULL);
  2126.  
  2127.     if (fclose (fp) < 0)
  2128.     error (0, errno, "cannot close %s", finfo->rcs->path);
  2129.  
  2130.     /* Now print out the data we have just computed.  */
  2131.     {
  2132.     unsigned int ln;
  2133.  
  2134.     for (ln = 0; ln < headlines.nlines; ++ln)
  2135.     {
  2136.         char buf[80];
  2137.         /* Period which separates year from month in date.  */
  2138.         char *ym;
  2139.         /* Period which separates month from day in date.  */
  2140.         char *md;
  2141.         RCSVers *prvers;
  2142.  
  2143.         prvers = headlines.vector[ln]->vers;
  2144.         if (prvers == NULL)
  2145.         prvers = vers;
  2146.  
  2147.         sprintf (buf, "%-12s (%-8.8s ",
  2148.              prvers->version,
  2149.              prvers->author);
  2150.         cvs_output (buf, 0);
  2151.  
  2152.         /* Now output the date.  */
  2153.         ym = strchr (prvers->date, '.');
  2154.         if (ym == NULL)
  2155.         cvs_output ("??-???-??", 0);
  2156.         else
  2157.         {
  2158.         md = strchr (ym + 1, '.');
  2159.         if (md == NULL)
  2160.             cvs_output ("??", 0);
  2161.         else
  2162.             cvs_output (md + 1, 2);
  2163.  
  2164.         cvs_output ("-", 1);
  2165.         cvs_output (month_printname (ym + 1), 0);
  2166.         cvs_output ("-", 1);
  2167.         /* Only output the last two digits of the year.  Our output
  2168.            lines are long enough as it is without printing the
  2169.            century.  */
  2170.         cvs_output (ym - 2, 2);
  2171.         }
  2172.         cvs_output ("): ", 0);
  2173.         cvs_output (headlines.vector[ln]->text, 0);
  2174.         cvs_output ("\n", 1);
  2175.     }
  2176.     }
  2177.  
  2178.     if (!ishead)
  2179.     {
  2180.     linevector_free (&curlines);
  2181.     linevector_free (&headlines);
  2182.     }
  2183.     block_free ();
  2184.     return 0;
  2185.  
  2186.   l_error:
  2187.     if (ferror (fp))
  2188.     error (1, errno, "cannot read %s", finfo->rcs->path);
  2189.     else
  2190.         error (1, 0, "%s does not appear to be a valid rcs file",
  2191.            finfo->rcs->path);
  2192.     /* Shut up gcc -Wall.  */
  2193.     return 0;
  2194. }
  2195.  
  2196. static const char *const annotate_usage[] =
  2197. {
  2198.     "Usage: %s %s [-l] [files...]\n",
  2199.     "\t-l\tLocal directory only, no recursion.\n",
  2200.     NULL
  2201. };
  2202.  
  2203. /* Command to show the revision, date, and author where each line of a
  2204.    file was modified.  Currently it will only show the trunk, all the
  2205.    way to the head, but it would be useful to enhance it to (a) allow
  2206.    one to specify a revision, and display only as far as that (easy;
  2207.    just have annotate_fileproc set all the ->vers fields to NULL when
  2208.    you hit that revision), and (b) handle branches (not as easy, but
  2209.    doable).  The user interface for both (a) and (b) could be a -r
  2210.    option.  */
  2211.  
  2212. int
  2213. annotate (argc, argv)
  2214.     int argc;
  2215.     char **argv;
  2216. {
  2217.     int local = 0;
  2218.     int c;
  2219.  
  2220.     if (argc == -1)
  2221.     usage (annotate_usage);
  2222.  
  2223.     optind = 0;
  2224.     while ((c = getopt (argc, argv, "+l")) != -1)
  2225.     {
  2226.     switch (c)
  2227.     {
  2228.         case 'l':
  2229.         local = 1;
  2230.         break;
  2231.         case '?':
  2232.         default:
  2233.         usage (annotate_usage);
  2234.         break;
  2235.     }
  2236.     }
  2237.     argc -= optind;
  2238.     argv += optind;
  2239.  
  2240. #ifdef CLIENT_SUPPORT
  2241.     if (client_active)
  2242.     {
  2243.     start_server ();
  2244.     ign_setup ();
  2245.  
  2246.     if (local)
  2247.         send_arg ("-l");
  2248.     send_file_names (argc, argv, SEND_EXPAND_WILD);
  2249.     /* FIXME:  We shouldn't have to send current files, but I'm not sure
  2250.        whether it works.  So send the files --
  2251.        it's slower but it works.  */
  2252.     send_files (argc, argv, local, 0);
  2253.     send_to_server ("annotate\012", 0);
  2254.     return get_responses_and_close ();
  2255.     }
  2256. #endif /* CLIENT_SUPPORT */
  2257.  
  2258.     return start_recursion (annotate_fileproc, (FILESDONEPROC) NULL,
  2259.                 (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL,
  2260.                 argc, argv, local, W_LOCAL, 0, 1, (char *)NULL,
  2261.                 1, 0);
  2262. }
  2263.